package com.study.easyexcel.api.listener;

import com.alibaba.excel.annotation.ExcelProperty;
import com.study.easyexcel.api.annotation.ExcelBind;
import com.study.easyexcel.common.constant.ExcelConstants;
import com.study.easyexcel.common.enums.ExcelPattern;
import com.study.easyexcel.common.exception.CheckedException;
import com.study.easyexcel.common.valid.ValidationProcedure;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据验证，数据验证不符合规则将直接抛出{@link CheckedException}
 *
 * @author yuhuan
 * @date 2023/5/12
 */
public interface ValidListener<T> {

    /**
     * 根据实体标注的{@link ExcelProperty}获取表头的名称
     *
     * @param pojoClass 要验证的数据类型
     * @return 表头列表
     */
    default List<String> getValidHead(Class<T> pojoClass) {
        List<String> excelPropertyNames = new ArrayList<>();
        Class<?> currentClass = pojoClass;

        // 循环获取当前类及其父类的字段，直到父类为 Object 类为止
        while (currentClass != null && currentClass != Object.class) {
            Field[] declaredFields = currentClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                boolean isExcelProperty = declaredField.isAnnotationPresent(ExcelProperty.class);
                if (!isExcelProperty) {
                    continue;
                }
                ExcelProperty excelProperty = declaredField.getAnnotation(ExcelProperty.class);
                String[] names = excelProperty.value();
                excelPropertyNames.add(names[names.length - 1]);
            }
            // 获取父类
            currentClass = currentClass.getSuperclass();
        }

        return excelPropertyNames;
    }

    /**
     * 根据实体字段标注的{@link ExcelBind}进行数据验证
     *
     * @param pojoData 要验证的数据
     * @throws CheckedException 存在不符合规则的数据
     */
    default void validAnalysis(T pojoData) throws CheckedException {
        Class<?> pojoClass = pojoData.getClass();
        Field[] declaredFields = pojoClass.getDeclaredFields();
        // 循环每个字段
        for (Field declaredField : declaredFields) {
            boolean isBind = declaredField.isAnnotationPresent(ExcelBind.class);
            if (!isBind) {
                continue;
            }
            this.validField(pojoData, declaredField);
        }
    }

    /**
     * 字段验证
     *
     * @param declaredField 字段信息
     * @param pojoData      要验证的数据
     * @throws CheckedException 字段值不符合规则
     */
    default void validField(T pojoData, Field declaredField) throws CheckedException {
        ReflectionUtils.makeAccessible(declaredField);
        Object fv = ReflectionUtils.getField(declaredField, pojoData);
        ExcelBind fieldBind = declaredField.getAnnotation(ExcelBind.class);
        if (fieldBind.cut()) {
            // 将数据分割验证
            if (ObjectUtils.isEmpty(fv)) {
                return;
            }
            String[] split = ((String) fv).split(ExcelConstants.EXCEL_HYPHEN);
            for (String cutValue : split) {
                validFieldValue(declaredField, cutValue);
            }
        } else {
            validFieldValue(declaredField, fv);
        }
    }

    /**
     * 值验证
     *
     * @param declaredField 字段信息
     * @param fv            要验证的值
     * @throws CheckedException 验证值不符合规则
     */
    default void validFieldValue(Field declaredField, Object fv) throws CheckedException {
        ExcelBind fieldBind = declaredField.getAnnotation(ExcelBind.class);
        ExcelPattern[] patterns = fieldBind.value();
        String[] messages = fieldBind.message();
        // 规则验证
        for (int i = 0; i < patterns.length; i++) {
            // 获取自定义消息
            String msg = "";
            if (messages.length > i) {
                msg = messages[i];
            }
            ruleRouting(fieldBind, patterns[i], fv, msg);
        }
    }

    /**
     * 校验规则匹配
     *
     * @param fieldBind 规则绑定器
     * @param factor    匹配因子
     * @param fv        要验证的值
     * @param msg       未通过校验的异常消息
     * @throws CheckedException 未通过校验
     */
    default void ruleRouting(ExcelBind fieldBind, ExcelPattern factor, Object fv, String msg) throws CheckedException {
        switch (factor) {
            case NOTNULL:
                notNull(fieldBind.name(), fv, msg);
                break;
            case SIZE:
                charSize(fieldBind, fv, msg);
                break;
            case PATTERN:
                pattern(fieldBind, fv, msg);
                break;
            default:
        }
    }

    /**
     * 空值校验
     *
     * @param nickname 字段的昵称{@link ExcelBind#name()}
     * @param fv       要验证的值
     * @param errMsg   未通过校验的异常消息
     * @throws CheckedException 值为空
     */
    default void notNull(String nickname, Object fv, String errMsg) throws CheckedException {
        ValidationProcedure.build().notNull(nickname, fv, errMsg);
    }

    /**
     * 字符长度校验
     *
     * @param fieldBind 规则绑定器
     * @param fv        要验证的值
     * @param errMsg    未通过校验的异常消息
     * @throws CheckedException 字符长度不符合绑定器规定的值
     */
    default void charSize(ExcelBind fieldBind, Object fv, String errMsg) throws CheckedException {
        ValidationProcedure.build().charSize(fieldBind.name(), fv, fieldBind.maxLen(), fieldBind.minLen(), errMsg);
    }

    /**
     * 正则匹配校验
     *
     * @param fieldBind 规则绑定器
     * @param fv        要验证的值
     * @param errMsg    未通过校验的异常消息
     * @throws CheckedException 字符不符合绑定器规定的正则匹配规则
     */
    default void pattern(ExcelBind fieldBind, Object fv, String errMsg) {
        ValidationProcedure.build().pattern(fieldBind.name(), fv, fieldBind.regexes(), errMsg);
    }
}
